#pragma once
#include "../3rd/cpp-httplib/httplib.h"
#include "../3rd/rapidjson/include/rapidjson/document.h"
#include "utils/dbutils.hpp"
#include "utils/transutils.hpp"

using namespace httplib;
using namespace rapidjson;

class DonorFilter {
 public:
  static DonorFilter *GetInstance() {
    static DonorFilter filter{"", ""};
    return &filter;
  }
  std::string donorId;
  std::string donorName;
};

class Donor_ {
 public:
  std::string donorId;
  std::string donorName;
  std::string phoneNumber;
  std::string address;
  std::string email;

  template <typename Writer>
  void Serialize(Writer &writer) const {
    writer.StartObject();
    writer.String("donorId");
    writer.String(donorId.c_str());
    writer.String("donorName");
    writer.String(donorName.c_str());
    writer.String("phoneNumber");
    writer.String(phoneNumber.c_str());
    writer.String("address");
    writer.String(address.c_str());
    writer.String("email");
    writer.String(email.c_str());
    writer.EndObject();
  }
};

void Donor(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto currentPage = std::stoi(req.get_param_value("currentPage"));
  auto pageSize = std::stoi(req.get_param_value("pageSize"));
  auto offset = (currentPage - 1) * pageSize;

  auto sql = "select * from donors where donorName like " +
             FuzzyString(DonorFilter::GetInstance()->donorName);

  if (!DonorFilter::GetInstance()->donorId.empty()) {
    sql += " and donorId = " + Qstring(DonorFilter::GetInstance()->donorId);
  }
  sql += " limit " + std::to_string(offset) + ", " + std::to_string(pageSize) +
         ";";

  auto result = DBWrapper::GetInstance()->Select(sql);

  std::string json;
  json += "[";
  bool first = true;
  for (size_t i = 0; i < result->size(); ++i) {
    const auto &row = result->at(i);

    Donor_ donor{row[0], row[1], row[2], row[3], row[4]};
    if (!first) {
      json += ", ";
    }
    json += Object2Json(donor);
    first = false;
  }
  json += "]";

  res.status = 200;
  res.set_content(json, "application/json");
}

void DonorTotal(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  DonorFilter::GetInstance()->donorId = "";
  DonorFilter::GetInstance()->donorName = "";

  auto sql = "select count(*) from donors;";
  auto result = DBWrapper::GetInstance()->Select(sql);
  hassert(result.get()->size() > 0, "SELECT no rows");
  res.status = 200;
  res.set_content("{\"totalRow\": " + result->begin()->at(0) + "}",
                  "application/json");
}

void DonorAdd(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto donorId = Qstring("NULL");
  auto donorName = Qstring(doc["donorName"].GetString());
  auto phoneNumber = Qstring(doc["phoneNumber"].GetString());
  auto address = Qstring(doc["address"].GetString());
  auto email = Qstring(doc["email"].GetString());

  if (DBWrapper::GetInstance()->Insert(
          "donors", {donorId, donorName, phoneNumber, address, email})) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void DonorDelete(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  auto donorId = req.get_param_value("donorId");

  auto sql = "delete from donors where donorId=" + donorId + ";";

  if (DBWrapper::GetInstance()->Excute(sql)) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void DonorEdit(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto donorId = Qstring(doc["donorId"].GetString());
  auto donorName = Qstring(doc["donorName"].GetString());
  auto phoneNumber = Qstring(doc["phoneNumber"].GetString());
  auto address = Qstring(doc["address"].GetString());
  auto email = Qstring(doc["email"].GetString());

  auto sql = "delete from donors where donorId=" + donorId + ";";

  if (DBWrapper::GetInstance()->Excute(sql) &&
      DBWrapper::GetInstance()->Insert(
          "donors", {donorId, donorName, phoneNumber, address, email})) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void DonorSearch(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");
  DonorFilter::GetInstance()->donorId = req.get_param_value("donorId");
  DonorFilter::GetInstance()->donorName = req.get_param_value("donorName");

  auto sql = "select count(*) from donors where donorName like " +
             FuzzyString(DonorFilter::GetInstance()->donorName);

  if (!DonorFilter::GetInstance()->donorId.empty()) {
    sql += " and donorId = " + Qstring(DonorFilter::GetInstance()->donorId);
  }
  sql += ";";
  auto result = DBWrapper::GetInstance()->Select(sql);
  res.set_content("{\"totalRow\": " + result->at(0).at(0) + "}",
                  "application/json");
  res.status = 200;
}